#include <gt_common.h>
#include <gt_interrupter.h>

#include <iostream>
using std::cerr;
using std::endl;

#include <Windows.h>

DWORD WINAPI geterns::interrupter::back_thread_routine( LPVOID p )
{
	// reinterpret pointer
#define RP(pt) reinterpret_cast<interrupter *>(pt)
	// do nothing in these cases
	if (RP(p)->callback == NULL ||
		RP(p)->target_thread == NULL)
		return 0;
	// just wait
	Sleep(RP(p)->time_interval);
	// then suspend the target thread
	if (SuspendThread(RP(p)->target_thread) == -1){
		cerr << "SuspendThread(RP(p)->target_thread) failed" << endl;
		return 1;
	}
	// a "TestAndRun"
	void (*temp)() = NULL;
	InterlockedExchangePointer(&temp, RP(p)->callback);
	if (temp != NULL) temp();
	// resume the target thread
	if (ResumeThread(RP(p)->target_thread) == -1){
		cerr << "ResumeThread(RP(p)->target_thread) failed" << endl;
		return 1;
	}

	return 0;

#undef RP
}

geterns::interrupter::interrupter( const DWORD t, void (*f)() ) :
	target_thread(NULL),
	back_thread(NULL),
	time_interval(t),
	callback(f)
{
	// get current thread
	target_thread = OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
	if (target_thread == NULL){
		cerr << "OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId()) failed" << endl;
	}
}

geterns::interrupter::~interrupter()
{
	// do NOT destruct while the back thread is still running
	if (back_thread != NULL){
		while (WaitForSingleObject(back_thread, 0) != WAIT_OBJECT_0){
			// bad behavior caused by improper use of this class.
			// so I force the caller to sleep for awhile
			cerr << "~interrupter() reporting: "
				"try to leave the class's scope "
				"while its back thread is still "
				"running"
				<< endl;
			Sleep(1000);
		}
	}
	if (target_thread != NULL) CloseHandle(target_thread);
}

bool geterns::interrupter::start()
{
	// check running state
	if (back_thread != NULL){
		cerr << "interrupter seems already in running state" << endl;
		return false;
	}
	//
	back_thread = CreateThread(0, 0, back_thread_routine, this, 0, NULL);
	return (back_thread != NULL);
}

bool geterns::interrupter::cancel()
{
	//
	InterlockedExchangePointer(&callback, NULL);

	return true;
}
